/*-------------------------------------------------------------------------
 * Filename:      util.c
 * Version:       $Id: util.c,v 1.1 2008/04/14 02:17:49 yuxu Exp $
 * Copyright:     Copyright (C) 1999, Jan-Derk Bakker
 * Author:        Jan-Derk Bakker <J.D.Bakker@its.tudelft.nl>
 * Description:   Simple utility functions for blob
 * Created at:    Wed Aug 25 21:00:00 1999
 * Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
 * Modified at:   Sun Oct  3 21:10:43 1999
 *-----------------------------------------------------------------------*/
/*
 * util.c: Simple utility functions for blob
 *
 * Copyright (C) 1999  Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#include "config.h"

#include "types.h"
#include "util.h"
#include "serial.h"
#include "irq.h"
#include "time.h"

u32 MyStrLen(const char *string)
{
	int len;

	for( len = 0; *(string+len) != '\0'; len++) {

	}
	return len;
}



void MyMemCpy(u32 *dest, const u32 *src, int numWords)
{
	while(numWords--) {
		*dest++ = *src++;
	}
} /* MyMemCpy */


void MyMemCpyChar(u8 *dest, const u8 *src, int numBytes)
{
	int i;

	for(i=0;i<numBytes;i++) dest[i]=src[i];

}


void MyMemSet(u32 *dest, const u32 wordToWrite, u32 numWords)
{
	u32 *limit = (u32 *)((u32)dest + numWords);

	while(dest < limit)
		*dest++ = wordToWrite;
} /* MyMemSet */

void MyMemSetChar(u8 *dest, const u8 charToWrite, int numChars)
{
	u8 *limit = dest + numChars;

	while(dest < limit)
		*dest++ = charToWrite;
}


int MyStrNCmp(const char *s1, const char *s2, int maxlen)
{
	int i;

	for(i = 0; i < maxlen; i++) {
		if(s1[i] != s2[i])
			return ((int) s1[i]) - ((int) s2[i]);
		if(s1[i] == 0)
			return 0;
	}

	return 0;
} /* MyStrNCmp */


/* returns 1 if equal */
int MyStrCmp(const char *s1, const char *s2, int maxlen)
{
	int i;

	for(i = 0; i < maxlen; i++) {
		if(s1[i] != s2[i])
			return 0;
		if(s1[i] == 0)
			return 1;
	}

	return 1;
} /* MyStrNCmp */

/* emulation of libc for strcmp */
int StrCmp(const char *s1, const char *s2)
{
	int i;
	int maxlen = 0x00100000; // 1MB

	for( i = 0; i < maxlen; i++) {
		if(s1[i] != s2[i]) {
			if((unsigned char)s1[i] < (unsigned char)s2[i])
				return -1;
			else
				return 1;
		}

		if(s1[i] == 0)
			return 0;
	}
	return 0;
}

int digitvalue(char isdigit)
{
	if (isdigit >= '0' && isdigit <= '9' )
		return isdigit - '0';
	else
		return -1;
}

int xdigitvalue(char isdigit)
{
	if (isdigit >= '0' && isdigit <= '9' )
		return isdigit - '0';
	if (isdigit >= 'a' && isdigit <= 'f')
		return 10 + isdigit - 'a';
	return -1;
}

/* convert a string to an u32 value. if the string starts with 0x, it
 * is a hexidecimal string, otherwise we treat is as decimal. returns
 * the converted value on success, or -1 on failure. no, we don't care
 * about overflows if the string is too long.
 */
int strtou32(const char *str, u32 *value)
{
	int i;

	*value = 0;

	if(MyStrNCmp(str, "0x", 2) == 0) {
		/* hexadecimal mode */
		str += 2;

		while(*str != '\0') {
			if((i = xdigitvalue(*str)) < 0)
				return -1;

			*value = (*value << 4) | (u32)i;
			str++;
		}
	} else {
		/* decimal mode */
		while(*str != '\0') {
			if((i = digitvalue(*str)) < 0)
				return -1;

			*value = (*value * 10) + (u32)i;
			str++;
		}
	}

	return 0;
}


int MyToUpper(int c)
{
	if((c >= 'a') && (c <= 'z'))
		c += 'A' - 'a';

	return(c);
}




int MyToLower(int c)
{
	if((c >= 'A') && (c <= 'Z'))
		c += 'a' - 'A';

	return(c);
}

void Hex2Str3(char Hex, char* String3)
{
  int mod,rem;

  mod = Hex/100;
  rem = Hex%100;
  if(mod==0) String3[0]=' ';
  else String3[0]=mod+'0';

  mod = rem/10;
  rem = rem%10;
  if((String3[0]==' ') && (mod==0)) String3[1]=' ';
  else String3[1]=mod+'0';

  mod = rem;
  String3[2]=mod+'0';
}

#if 0	/* Nam9, 2004. 9. 3 */

/*
 * printk()
 */
#define  _AUPBND         (sizeof (unsigned int) - 1)
#define  _ADNBND         (sizeof (unsigned int) - 1)
#define _bnd(X, bnd)    (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T)   (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)      (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

int t_isdigit(char n)
{
	unsigned int flags;
	flags = 0xff & n;

	if ( (flags > 0x29) && (flags < 0x3A) )
		return 1;
	else
		return 0;
}



static int skip_atoi(const char **s)
{
	int i=0;

	while (t_isdigit(**s))
		i = i*10 + *((*s)++) - '0';
	return i;
}

/* We're not 64-bit, but... */
#define do_div(n,base)                                          \
	({                                                      \
	int __res;                                              \
	__res = ((unsigned long)n) % (unsigned int)base;        \
	n = ((unsigned long)n) / (unsigned int)base;            \
	__res;                                                  \
	})

#define ZEROPAD	1		/* pad with zero */
#define SIGN	2		/* unsigned/signed long */
#define PLUS	4		/* show plus */
#define SPACE	8		/* space if plus */
#define LEFT	16		/* left justified */
#define SPECIAL	32		/* 0x */
#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */



int strnlen (const char *s, unsigned int maxlen)
{
	unsigned int count;
	char *p;

	p = (char *)s;
	count = 0 ;
	while ( count < maxlen ) {
		if (*p == '\0')
			break;
		count++;
		p++;
	}
	return count;
}

static char * number(char * buf, char * end, long long num, int base, int size, int precision, int type)
{
	char c,sign,tmp[66];
	const char *digits;
	char small_digits[37];
	char large_digits[37];
	char *small_digits_p = small_digits;
	char *large_digits_p = large_digits;
	int i;

	small_digits_p = "0123456789abcdefghijklmnopqrstuvwxyz";
	large_digits_p = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	digits = (type & LARGE) ? large_digits_p : small_digits_p;
	if (type & LEFT)
		type &= ~ZEROPAD;
	if (base < 2 || base > 36)
		return 0;
	c = (type & ZEROPAD) ? '0' : ' ';
	sign = 0;
	if (type & SIGN) {
		if (num < 0) {
			sign = '-';
			num = -num;
			size--;
		} else if (type & PLUS) {
			sign = '+';
			size--;
		} else if (type & SPACE) {
			sign = ' ';
			size--;
		}
	}
	if (type & SPECIAL) {
		if (base == 16)
			size -= 2;
		else if (base == 8)
			size--;
	}
	i = 0;
	if (num == 0)
		tmp[i++]='0';
	else while (num != 0)
		tmp[i++] = digits[do_div(num,base)];
	if (i > precision)
		precision = i;
	size -= precision;
	if (!(type&(ZEROPAD+LEFT))) {
		while(size-->0) {
			if (buf <= end)
				*buf = ' ';
			++buf;
		}
	}
	if (sign) {
		if (buf <= end)
			*buf = sign;
		++buf;
	}
	if (type & SPECIAL) {
		if (base==8) {
			if (buf <= end)
				*buf = '0';
			++buf;
		} else if (base==16) {
			if (buf <= end)
				*buf = '0';
			++buf;
			if (buf <= end)
				*buf = digits[33];
			++buf;
		}
	}
	if (!(type & LEFT)) {
		while (size-- > 0) {
			if (buf <= end)
				*buf = c;
			++buf;
		}
	}
	while (i < precision--) {
		if (buf <= end)
			*buf = '0';
		++buf;
	}
	while (i-- > 0) {
		if (buf <= end)
			*buf = tmp[i];
		++buf;
	}
	while (size-- > 0) {
		if (buf <= end)
			*buf = ' ';
		++buf;
	}
	return buf;
}

/**
* vsnprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @args: Arguments for the format string
*
* Call this function if you are already dealing with a va_list.
* You probably want snprintf instead.
 */
int vsnprintf(char *buf, unsigned int size, const char *fmt, char *args)
{
	int len;
	unsigned long long num;
	int i, base;
	char *str, *end, c;
	const char *s;

	int flags;		/* flags to number() */

	int field_width;	/* width of output field */
	int precision;		/* min. # of digits for integers; max
				   number of chars for from string */
	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
				/* 'z' support added 23/7/1999 S.H.    */
				/* 'z' changed to 'Z' --davidm 1/25/99 */

	str = buf;
	end = buf + size - 1;

	if (end < buf - 1) {
		end = ((void *) -1);
		size = end - buf + 1;
	}

	for (; *fmt ; ++fmt) {
		if (*fmt != '%') {
			if (str <= end)
				*str = *fmt;
			++str;
			continue;
		}

		/* process flags */
		flags = 0;
		repeat:
			++fmt;		/* this also skips first '%' */
			switch (*fmt) {
				case '-': flags |= LEFT; goto repeat;
				case '+': flags |= PLUS; goto repeat;
				case ' ': flags |= SPACE; goto repeat;
				case '#': flags |= SPECIAL; goto repeat;
				case '0': flags |= ZEROPAD; goto repeat;
			}

		/* get field width */
		field_width = -1;
		if (t_isdigit(*fmt))
			field_width = skip_atoi(&fmt);
		else if (*fmt == '*') {
			++fmt;
			/* it's the next argument */
			field_width = va_arg(args, int);
			if (field_width < 0) {
				field_width = -field_width;
				flags |= LEFT;
			}
		}

		/* get the precision */
		precision = -1;
		if (*fmt == '.') {
			++fmt;
			if (t_isdigit(*fmt))
				precision = skip_atoi(&fmt);
			else if (*fmt == '*') {
				++fmt;
				/* it's the next argument */
				precision = va_arg(args, int);
			}
			if (precision < 0)
				precision = 0;
		}

		/* get the conversion qualifier */
		qualifier = -1;
		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') {
			qualifier = *fmt;
			++fmt;
			if (qualifier == 'l' && *fmt == 'l') {
				qualifier = 'L';
				++fmt;
			}
		}

		/* default base */
		base = 10;

		switch (*fmt) {
			case 'c':
				if (!(flags & LEFT)) {
					while (--field_width > 0) {
						if (str <= end)
							*str = ' ';
						++str;
					}
				}
				c = (unsigned char) va_arg(args, int);
				if (str <= end)
					*str = c;
				++str;
				while (--field_width > 0) {
					if (str <= end)
						*str = ' ';
					++str;
				}
				continue;

			case 's':
				s = va_arg(args, char *);
				if (!s)
					s = "<NULL>";

				len = strnlen(s, precision);

				if (!(flags & LEFT)) {
					while (len < field_width--) {
						if (str <= end)
							*str = ' ';
						++str;
					}
				}
				for (i = 0; i < len; ++i) {
					if (str <= end)
						*str = *s;
					++str; ++s;
				}
				while (len < field_width--) {
					if (str <= end)
						*str = ' ';
					++str;
				}
				continue;

			case 'p':
				if (field_width == -1) {
					field_width = 2*sizeof(void *);
					flags |= ZEROPAD;
				}
				str = number(str, end,
						(unsigned long) va_arg(args, void *),
						16, field_width, precision, flags);
				continue;


			case 'n':
				/* FIXME:
				* What does C99 say about the overflow case here? */
				if (qualifier == 'l') {
					long * ip = va_arg(args, long *);
					*ip = (str - buf);
				} else if (qualifier == 'Z') {
					unsigned int * ip = va_arg(args, unsigned int *);
					*ip = (str - buf);
				} else {
					int * ip = va_arg(args, int *);
					*ip = (str - buf);
				}
				continue;

			case '%':
				if (str <= end)
					*str = '%';
				++str;
				continue;

				/* integer number formats - set up the flags and "break" */
			case 'o':
				base = 8;
				break;

			case 'X':
				flags |= LARGE;
			case 'x':
				base = 16;
				break;

			case 'd':
			case 'i':
				flags |= SIGN;
			case 'u':
				break;

			default:
				if (str <= end)
					*str = '%';
				++str;
				if (*fmt) {
					if (str <= end)
						*str = *fmt;
					++str;
				} else {
					--fmt;
				}
				continue;
		}
		if (qualifier == 'L')
			num = va_arg(args, long long);
		else if (qualifier == 'l') {
			num = va_arg(args, unsigned long);
			if (flags & SIGN)
				num = (signed long) num;
		} else if (qualifier == 'Z') {
			num = va_arg(args, unsigned int);
		} else if (qualifier == 'h') {
			num = (unsigned short) va_arg(args, int);
			if (flags & SIGN)
				num = (signed short) num;
		} else {
			num = va_arg(args, unsigned int);
			if (flags & SIGN)
				num = (signed int) num;
		}
		str = number(str, end, num, base,
				field_width, precision, flags);
	}
	if (str <= end)
		*str = '\0';
	else if (size > 0)
		/* don't write out a null byte if the buf size is zero */
		*end = '\0';
	/* the trailing null byte doesn't count towards the total
	* ++str;
	*/
	return str-buf;
}



int printf(const char *fmt, ...)
{
	char *p;

	char *args;
	int printed_len;
	static char printk_buf[1024];

        va_start(args, fmt);
	printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
        va_end(args);

	for (p = printk_buf; *p; p++) {
      		SerialOutputByte(*p);
	}

	return printed_len;
}

#endif

char *MyStrCpy(u8 *dest, const char *src)
{
	int i = 1024; // 1KB
	char *orgp = dest;

	while( (*src) != '\0' ) {
		*dest++ = *src++;
		i--;
		if ( i == 0 ) {
			printk("MyStrCpy: Can't find NULL character in src\n");
			break;
		}
	}
	*dest = '\0';
	return orgp;
}

char *MyStrChar(const char *s, char c)
{
	int i = 10240; // 10KB
	char *targetp = NULL;

	while( (*s) != '\0' ) {
		if ( (*s) == c )
			targetp = (char *)s;

		s++;
		i--;
		if ( i == 0 ) {
			printk("MyStrCpy: Can't find NULL character in src\n");
			break;
		}
	}
	return targetp;
}


#if 0	/* Nam9, 2004. 9. 3*/


#if 1 // would be updated by tium
#define SIZE_10K	10240
#define SIZE_100K	102400
#define SIZE_512K	0x00080000
#define SIZE_1024K	0x00100000

#define N_10K	100
#define N_100K	10
#define N_512K	2
#define N_1024K	2
struct alloc_flag_struct {
	u8 flag_10k[N_10K];
	u8 flag_100k[N_100K];
	u8 flag_512k[N_512K];
	u8 flag_1024k[N_1024K];
};
static struct alloc_flag_struct my_flag;

#define	START_ADDR_10K (RAM_START + RAM_BOOT_LOADER_AREA_START_OFFSET_FROM_RAM_START - (SIZE_10K * N_10K) )
#define	START_ADDR_100K (START_ADDR_10K - (SIZE_100K * N_100K) )
#define	START_ADDR_512K (START_ADDR_100K - (SIZE_512K * N_512K) )
#define	START_ADDR_1024K (START_ADDR_512K - (SIZE_1024K * N_1024K) )

void *MyMalloc(u32 nbytes)
{
	int i;
	u32 alloc_address;

	alloc_address = 0;
	if (nbytes < SIZE_10K) {
		for (i = 0; i < N_10K; i++) {
			if (my_flag.flag_10k[i] == 0) {
				my_flag.flag_10k[i] = 1;
				alloc_address = START_ADDR_10K + SIZE_10K * i;
				break;
			}
		}
	} else if (nbytes < SIZE_100K) {
		for (i = 0; i < N_100K; i++) {
			if (my_flag.flag_100k[i] == 0) {
				my_flag.flag_100k[i] = 1;
				alloc_address = START_ADDR_100K + SIZE_100K * i;
				break;
			}
		}
	} else if (nbytes < SIZE_512K) {
		for (i = 0; i < N_512K; i++) {
			if (my_flag.flag_512k[i] == 0) {
				my_flag.flag_512k[i] = 1;
				alloc_address = START_ADDR_512K + SIZE_512K * i;
				break;
			}
		}
	} else if (nbytes < SIZE_1024K) {
		for (i = 0; i < N_1024K; i++) {
			if (my_flag.flag_1024k[i] == 0) {
				my_flag.flag_1024k[i] = 1;
				alloc_address = START_ADDR_1024K + SIZE_1024K * i;
				break;
			}
		}
	} else {
		printk("Request Area is Too big!\n");
	}

	return (void *)alloc_address;
}

void MyFree(u32 address)
{
	int i;

	if (address < START_ADDR_1024K) { // address : too small
		printk("address 0x%08x is too small\n", address);
	} else if (address < START_ADDR_512K) { // adress : 1024 area
		for (i = 0; i < N_1024K; i++) {
			if (address < (START_ADDR_1024K + SIZE_1024K * (i + 1))) {
				my_flag.flag_1024k[i] = 0;
				break;
			}
		}
	} else if (address < START_ADDR_100K) { // adress : 512 area
		for (i = 0; i < N_512K; i++) {
			if (address < (START_ADDR_512K + SIZE_512K * (i + 1))) {
				my_flag.flag_512k[i] = 0;
				break;
			}
		}
	} else if (address < START_ADDR_10K) { // adress : 100 area
		for (i = 0; i < N_100K; i++) {
			if (address < (START_ADDR_100K + SIZE_100K * (i + 1))) {
				my_flag.flag_100k[i] = 0;
				break;
			}
		}
	} else if (address < START_ADDR_10K + (SIZE_10K * N_10K)) { // adress : 10 area
		for (i = 0; i < N_10K; i++) {
			if (address < (START_ADDR_10K + SIZE_10K * (i + 1))) {
				my_flag.flag_10k[i] = 0;
				break;
			}
		}
	} else {
		printk("address 0x%08x is too big\n", address);
	}
}
#endif


#endif


